//
//  STDSShaderProgram.cpp
//  STDRenderSDK
//
//  Created by 陈学明 on 2023/3/12.
//

#include "STDSShaderProgram.hpp"


static bool compileShader(GLuint s) {
    GLint status;
    glCompileShader(s);
    glGetShaderiv(s, GL_COMPILE_STATUS, &status);
    if (!status) {
        GLchar InfoLog[1024];
        glGetShaderInfoLog(s, 1024, NULL, InfoLog);
        string Log = string(InfoLog);
        LOG_DEBUG(">>>>Error compiling shader: %s\n", InfoLog);
        return false;
    }
    return true;
}

STDSShaderProgram::STDSShaderProgram(const std::string &vex, const std::string &frg) {
    mbInited = false;
    mProgId = 0;
    load(vex, frg);
}

STDSShaderProgram::~STDSShaderProgram() {
    destroy();
//    printf("dealloc--STDSShaderProgram\n");
}


void STDSShaderProgram::load(const std::string &vex, const std::string &frg) {
    mStrVex = vex;
    mStrFra = frg;
    mbInited = false;
}

void STDSShaderProgram::load(const char *vex, const char *frg) {
    mStrVex = vex;
    mStrFra = frg;
    mbInited = false;
}

bool STDSShaderProgram::init() {
    if (mbInited)
        return true;
    /*Create Shader*/
    GLint vert = glCreateShader(GL_VERTEX_SHADER);
    GLint frag = glCreateShader(GL_FRAGMENT_SHADER);
    const char *_ver[1];
    const char *_fra[1];
    _ver[0] = mStrVex.c_str();
    _fra[0] = mStrFra.c_str();
    glShaderSource(vert, 1, _ver, NULL);
    glShaderSource(frag, 1, _fra, NULL);
    bool res = compileShader(vert);
    if (false == res) {
        LOG_DEBUG(">>>>Vertex Shader Code: %s\n", _ver[0]);
        return false;
    }
    res = compileShader(frag);
    if (false == res) {

        LOG_DEBUG("Fragment Shader Code: %s \n ", _fra[0]);
        return false;
    }
    /*Create Program*/
    mProgId = glCreateProgram();
    glAttachShader(mProgId, vert);
    glDeleteShader(vert);
    glAttachShader(mProgId, frag);
    glDeleteShader(frag);
    glLinkProgram(mProgId);
//    printf("GL>>>>STDSShaderProgram:%d\n", mProgId);
    GLint linked;
    glGetProgramiv(mProgId, GL_LINK_STATUS, &linked);
    if (!linked) {
        GLchar ErrorLog[1024] = {0};
        glGetProgramInfoLog(mProgId, sizeof(ErrorLog), NULL, ErrorLog);
        LOG_DEBUG("Error linking shader program: '%s'\n", ErrorLog);
        glDeleteProgram(mProgId);
        return false;
    }
    mbInited = true;
    return true;
}

void STDSShaderProgram::destroy() {
    if (!mbInited)
        return;
    if((bool)glIsProgram(mProgId)){
        glDeleteProgram(mProgId);
    }
    mProgId = 0;
    mbInited = false;
}

int STDSShaderProgram::getAttrib(const char *name) const {
    return glGetAttribLocation(mProgId, name);
}

int STDSShaderProgram::getUniform(const string &name) const {
    return glGetUniformLocation(mProgId, name.c_str());
}

void STDSShaderProgram::use() {
    if (!mbInited) {
        init();
    }
    glUseProgram(mProgId);
}

void STDSShaderProgram::unuse() {
    glUseProgram(0);
}

void STDSShaderProgram::setMatrix2(int id, float *matrix) {
    glUniformMatrix2fv(id, 1, GL_FALSE, matrix);
}

void STDSShaderProgram::setMatrix2(int id,  int count, float *matrix) {
    glUniformMatrix2fv(id, count, GL_FALSE, matrix);
}

void STDSShaderProgram::setMatrix3(int id, float *matrix) {
    glUniformMatrix3fv(id, 1, GL_FALSE, matrix);
}

void STDSShaderProgram::setMatrix3(int id,  int count, float *matrix) {
    glUniformMatrix3fv(id, count, GL_FALSE, matrix);
}

void STDSShaderProgram::setMatrix(int id, const GLfloat *value) {
    glUniformMatrix4fv(id, 1, GL_FALSE, value);
}

void STDSShaderProgram::setUniform(int id, int value) {
    glUniform1i(id, value);
}

void STDSShaderProgram::setUniform(int id, float value) {
    glUniform1f(id, value);
}

void STDSShaderProgram::setUniform(int id, int n, float *value) {
    glUniform1fv(id, n, value);
}

void STDSShaderProgram::setUniform1iv(int id, int n, int *value) {
    glUniform1iv(id, n, value);
}

void STDSShaderProgram::setUniform1fv(int id, int n, float *value) {
    glUniform1fv(id, n, value);
}

void STDSShaderProgram::setUniform2fv(int id, int n, float *value) {
    glUniform2fv(id, n, value);
}

void STDSShaderProgram::setUniform3fv(int id, int n, float *value) {
    glUniform3fv(id, n, value);
}

void STDSShaderProgram::setUniform4fv(int id, int n, float *value) {
    glUniform4fv(id, n, value);
}

void STDSShaderProgram::setUniform2f(int id, float valueX, float valueY) {
    glUniform2f(id, valueX, valueY);
}

void STDSShaderProgram::setUniform3f(int id, float valueX, float valueY, float valueZ) {
    glUniform3f(id, valueX, valueY, valueZ);
}

void STDSShaderProgram::setUniform4f(int id, float valueX, float valueY, float valueZ, float valueW) {
    glUniform4f(id, valueX, valueY,valueZ,valueW);
}
